// 第二部分 this和对象原型
// 第五章 原型
// JS中的对象有一个特殊的[[Prototype]]内置属性，其实是 对于其他对象的引用
// 几乎所有的对象在创建时[[Prototype]]属性都会被赋予一个非空的值
// [[Get]]操作
// 1.检查对象本身是否有这个属性，有的话就使用
// 2.如果无法找到，就会继续访问对象的[[Prototype]]链
// 3.这个过程会持续找到匹配的属性名或者查完整条[[Prototype]]链，如果是后者的话，[[Get]]操作返回的值是undefined

// 使用for..in遍历对象时原理和查找[[Prototype]]链类似，任何可以通过原型链访问到的属性都会被枚举
// in操作符检查属性对象是否存在时，也会查找对象的整条原型链，无论是否枚举

// [[Prototype]]的尽头 ==> Object.prototype
// 所有普通的[[Prototype]]链都会指向内置的Object.prototype对象
// 这个内置对象包含很多js通用的功能

// 属性设置和屏蔽
var myobj = {};
myobj.foo = "bar";
// 如果对象中有foo的普通数据访问属性，赋值语句就只会修改已有的属性值
// 如果foo不是直接存在于myobj中，[[Prototype]]链就会被遍历，如果链上找不到，foo就会被直接添加到myobj上
// 如果foo存在于原型链上层 。。。下面讲解
// 如果foo即出现在myobj中也出现在myobj的[[Prototype]]链上层，那么就会发生屏蔽，myobj上的foo会屏蔽上层所有的foo属性

// 这里讲解
// 1.如果[[Prototype]]链上层存在foo，并且可写(writable:true)，那就会直接在myobj中添加一个新的foo属性，是屏蔽属性
// 2.如果[[Prototype]]链上层存在foo，并且只读(writable:false)，严格模式下会报错，否则赋值语句会被忽略，不会发生屏蔽
// 3.如果[[Prototype]]链上层存在foo，并且它是一个setter，那就一定会调用这个setter。foo不会添加到myobj，也不会重新定义这个setter
// 2、3情况下也屏蔽的话使用 Object.defineProperty(..)赋值

// JS中只有对象，没有类

// 类函数
function Foo() {}
var a = new Foo();
Object.getPrototypeOf(a) === Foo.prototype; // true
// 调用new Foo()时会创建a，其中一步就是给a一个内部的[[Prototype]]链接，关联到Foo.prototype指向的那个对象
// 最后得到了两个对象，他们之间互相关联
// 在js中不能创建一个类的多个实例，只能创建多个对象，他们[[Prototype]]关联的是同一个对象，默认情况下不会复制，因此这些对象不会失去联系

// "构造函数" ==> 所有带new的函数调用
function Foo() {}
Foo.prototype.constructor === Foo; // true
var a = new Foo();
console.log(a.constructor === Foo);
// true
Foo.prototype = {};
var a1 = new Foo();
console.log(a1.constructor === Foo);
console.log(a1.constructor === Object);
// a1 并没有 .constructor 属性，所以它会委托 [[Prototype]] 链上的 Foo. prototype。
// 但是这个对象也没有 .constructor 属性(不过默认的 Foo.prototype 对象有这 个属性!)，
// 所以它会继续委托，这次会委托给委托链顶端的 Object.prototype。这个对象 有 .constructor 属性，指向内置的 Object(..) 函数
